<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <p>1</p>
  <button onclick="myFunc()">点击</button>
  <button>测试测试</button>

  <div id="a">
    <div id="b"></div>
  </div>
  <script>
    // 1、
    var one;
    var two = null;
    console.log(one == two, one === two); // true false
    /*
    one的变量的值是undefined two的变量的值是null 但undefined的类型是undefined，null的类型是object
    one==two 是 loose comparison, 只比较值, 不比较类型, 所以为 true
    one===two 是 strict comparison, 既比较值, 又比较类型, 所以为 faluse 
    */

    //2、以下代码执行后，浏览器打印出来的结果分别是 green blue
    var color = 'green'
    var test = {
      color: 'blueconsole.log',
      getColor() {
        var color = 'red'
        console.log(this.color)
      }
    }
    var getColor = test.getColor
    getColor()
    test.getColor()
    /*
    知识点一:js函数调用时加括号和不加括号的区别.不加括号相当于把函数代码赋给等号左边,加括号是把函数返回值赋给等号左边
    知识点二:js中this的用法,this总是指向调用它的对象,getColor为windows调用的,倒数第一行的getColor是test对象调用的
    getColor();相当于普通的函数调用，此时this指向window，this.color应该为全局变量的值
    test.getColor();此时this指向调用函数的对象test，因此this.color应该为对象的属性值
    */

    // 3、
    function myFunc() {
      document.write(a);
      document.write(func());
      var a = 1;
      function func() {
        return 2;
      }
    }
    /*
    点击按钮后页面上的最终输出结果，所以原来页面上的p标签的1，会被document.write函数输出的内容覆盖。所以没有1。
    打印a是undefined是因为var a的时候进行了变量提升，但是要执行到那一步才会真的赋值。
    */

    //4、控制台打印结果  0 1 2 2
    for (let i = 0; i < 2; i++) {
      setTimeout(function () {
        console.log(i)
      }, 100
      );
    }
    for (var i = 0; i < 2; i++) {
      setTimeout(function () {
        console.log(i)
      }, 100
      );
    }
    /*
    第一个：let将i绑定到for循环快中，事实上它将其重新绑定到循环体的每一次迭代中，确保上一次迭代结束的值重新被赋值。
    setTimeout里面的function()属于一个新的域，通过 var 定义的变量是无法传入到这个函数执行域中的，
    通过使用 let 来声明块变量，这时候变量就能作用于这个块，所以 function就能使用 i 这个变量了；输出为0,1.

    第二个：setTimeout是异步执行，1s后往异步任务队列里面添加一个任务，只有同步的全部执行完，才会执行异步任务队列里的任务，
    当主线执行完成后，i是2，所以此时再去执行任务队列里的任务时，所以输出两次2.
    */

    // 5、以下代码执行后，console输出的信息是
    for (var i = 0; i < 5; i++) {
      requestAnimationFrame(() => console.log(i)); // 5 5 5 5 5
    }
    for (let i = 0; i < 5; i++) {
      requestAnimationFrame(() => console.log(i)); // 0 1 2 3 4
    }
    /*
    requestAnimationFrame并非同步执行的，具体可查阅其实现细节，
    所以for循环中的requestAnimationFrame会在循环退出的时候才执行其中的回调，注意循环退出的时候 i 的值为 5.
    更应该注意var和let，换成let输出的就是01234。var时是函数作用域，异步函数在for循环结束后还未执行，函数作用域的i变成了5。
    let执行时，因为块级作用域的缘故，每一次for循环都会产生一个块级作用域
    */

    // 6、假设有如下代码，那么a6(10)的返回结果是？（ 5 ）
    function a6(a) {
      a ^= (1 << 4) - 1;
      return a;
    }
    result6 = a6(10)
    console.log(result6) // 5
    /*
    1<<4   左移相当于1*2^4=16  <<表示左移，先化为2进制，1为01，然后左移4位，成010000，再化为10进制，10000(二进制) = 16(十进制)
    a^=16-1=15
    a=a^15=10^15
    ^ 异或运算：
    10的二进制00001010
    15的二进制00001111
    ========>00000101  转成十进制：5（按位异或运算，同为1或同为0取0，不同取1）
    */

    //7、
    var test = new Boolean();
    document.write(test);
    document.write("<br />"); // false

    var test = new Boolean(0);
    document.write(test);
    document.write("<br />"); // false

    var test = new Boolean(null);
    document.write(test);
    document.write("<br />"); // false

    var test = new Boolean("");
    document.write(test);
    document.write("<br />"); // false

    var test = new Boolean(NaN);
    document.write(test);
    document.write("<br />"); // false
    /*
    Boolean类型转换：
        1.对于String:只有非空字符串为真
        2.对于Number:除了0和NaN之外都为真
        3.对于Boolean:true为真
        4.对于Object:除了null之外都为真
        5.undefined为false;
    */

    // 8、
    var str1 = new RegExp("e");
    document.write(str1.exec("hello"));
    /*
    exec() 方法是一个正则表达式方法。
    exec() 方法用于检索字符串中的正则表达式的匹配。
    该函数返回一个数组，其中存放匹配的结果。如果未找到匹配，则返回值为 null。
    本题的exec执行结果是一个数组，但是使用了document.write，所以显示在html中的内容是数组中的元素，即e

    js还有一个test()方法，用于检测字符串是否匹配某个模式，返回值为boolean
    */

    //9、
    document.getElementById('a').addEventListener('click', e => { console.log(1) });
    document.getElementById('b').addEventListener('click', e => { e.preventDefault(); console.log(2) });
    // 2 1
    /*
    几个常见的事件的方法
    preventDefault()    取消事件默认行为，如阻止点击提交按钮时对表单的提交（本题中click并没有什么默认行为）
    stopImmediatePropagation()   取消事件冒泡同时阻止当前节点上的事件处理程序被调用，影响当前的事件***
    stopPropagation()   取消事件冒泡，不影响事件***
    cancelBubbe()     取消事件冒泡
    returnValue()      取消事件默认行为
    */

    // 10、
    var F = function () { };
    Object.prototype.a = function () { };
    Function.prototype.b = function () { };
    var f = new F();
    // f能取到a，但取不到b
    /*
    F函数是Function对象的一个实例，所以F.a可以访问到Function原型里面的属性
    new F()返回的是一个对象f，是Object的一个实例，没有继承Function的，所以无法访问Function原型里面的属性
    由于js里所有对象都是Object的实例，所以F函数可以访问到Object原型里面的属性
    */

    // 11、假设val已经声明,可定义为任何值。则下面js代码有可能输出的结果为:
    var val
    console.log('Value is ' + (val != '0') ? 'define' : 'undefine'); // define
    /*
    因为+运算符优先级大于?:运算符，所以代码执行顺序是('Value is ' + (val != '0')) ? 'define' : 'undefine'，
    而?前面的表达式运算结果为字符串'Value is true'，它被转换为布尔值是true，所以打印出来的结果是字符串'define'。
    */

    // 12、分析下面代码块，输出（ ）行########。
    var i = 0;
    while (i < 40) {
      if (i < 30)
        continue;
      Document.write('########');
      i++;
    }

  </script>
</body>

</html>